/*
 * CMapNode.cpp
 *
 *      Author: grifos
 */

#include "CMapNode.h"

CMapNode::CMapNode(CJoueur * j,CSkyBox * s) {

	_joueur = j;
	_skyBox = s;
	_bombe = new osg::Group;
	_root = new osg::MatrixTransform;
	_rootRot0 = new osg::MatrixTransform;
	_rootRot1 = new osg::MatrixTransform;
	_elementOnmap = new osg::Group;
	_root->setMatrix(osg::Matrix::rotate(osg::inDegrees(0.0f),0.0,1.0,0.0));
	_rootRot0->setMatrix(osg::Matrix::translate(0,_joueur->_ry,_joueur->_rz));
	_rootRot1->setMatrix(osg::Matrix::rotate(osg::inDegrees(-90.0f),1.0,0.0,0.0));
	createMapNode("./ressource/landscape-heightmap.bmp","./ressource/landscape-texture.bmp");

	_root->addChild(_rootRot0.get());
	_rootRot0->addChild(_rootRot1.get());
	_rootRot1->addChild(_map.get());
	_rootRot1->addChild(_bombe.get());
	_rootRot1->addChild(_elementOnmap.get());

	_treeElement = new CArbre();
	std::string model("./ressource/rock.3ds");
	_rockElement = new CObject3d(0.f,0.f,0.f,0.f,model,4.0f);
	std::string model2("./ressource/house.3ds");
	_chaletElement = new CObject3d(0.f,0.f,0.f,0.f,model2,2.0f);

	_phi = 0;
	_theta = 0;
	_omega = 0;
}

CMapNode::~CMapNode() {
	// TODO Auto-generated destructor stub
}

void CMapNode::createMapNode(std::string heightFile, std::string texFile)
{
	osg::Image* heightMap = osgDB::readImageFile(heightFile);

	osg::HeightField* heightField = new osg::HeightField();
	heightField->allocate(heightMap->s(), heightMap->t());
	heightField->setOrigin(osg::Vec3(-heightMap->s() / 2, -heightMap->t() / 2, 0));
	heightField->setXInterval(1.0f);
	heightField->setYInterval(1.0f);
	heightField->setSkirtHeight(1.0f);

	for (int r = 0; r < heightField->getNumRows(); r++) {
		for (int c = 0; c < heightField->getNumColumns(); c++) {
			float h= ((*heightMap->data(c, r)) / 255.0f) * 40.0f;
			heightField->setHeight(c, r, h);
		}
	}

	_map = new osg::Geode;
	_map->addDrawable(new osg::ShapeDrawable(heightField));

	osg::Texture2D* tex = new osg::Texture2D(osgDB::readImageFile(texFile));
	tex->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR_MIPMAP_LINEAR);
	tex->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
	tex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
	tex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
	_map->getOrCreateStateSet()->setTextureAttributeAndModes(0, tex);

}

osg::Vec3d CMapNode::calculHeight(osg::Vec3d pos)
{
	osg::LineSegment* posLocationSegment = new osg::LineSegment();
	posLocationSegment->set(osg::Vec3(pos.x(), pos.y(), 999),
			osg::Vec3(pos.x(), pos.y(), -999));

	osgUtil::IntersectVisitor findPosElevationVisitor;
	findPosElevationVisitor.addLineSegment(posLocationSegment);
	_map->accept(findPosElevationVisitor);

	osgUtil::IntersectVisitor::HitList posElevationLocatorHits;
	posElevationLocatorHits = findPosElevationVisitor.getHitList(
			posLocationSegment);
	osgUtil::Hit heightTestResults;

	if (posElevationLocatorHits.empty()) {
		//std::cerr << " couldn't place find position on terrain" << std::endl;
		// return -1;
	}

	heightTestResults = posElevationLocatorHits.front();
	osg::Vec3d terrainHeight = heightTestResults.getWorldIntersectPoint();

	return terrainHeight;
}

osg::Vec3d CMapNode::calculNormal(osg::Vec3d one, osg::Vec3d two, osg::Vec3d three)
{
    // Calcul des vecteur u et v
	osg::Vec3d u;
	u.x() = two.x() - one.x();
	u.y() = two.y() - one.y();
	u.z() = two.z() - one.z();

	osg::Vec3d v;
	v.x() = three.x() - two.x();
	v.y() = three.y() - two.y();
	v.z() = three.z() - two.z();

	//retour de la normal
	osg::Vec3d normal;
	normal.x() = u.y()*v.z() - u.z()*v.y();
	normal.y() = u.x()*v.z() - u.z()*v.x();
	normal.z() = u.x()*v.y() - u.y()*v.x();

    return normal;
}


void CMapNode::update(float delta)
{
	calculColision();
	_root->setMatrix(osg::Matrix::rotate(_joueur->getAngleVue(),0.0,1.0,0.0));
	_root->preMult(osg::Matrix::rotate(_phi,fabs(cos(_joueur->getAngleVue())),0.0,fabs(sin(_joueur->getAngleVue()))));
//	_root->preMult(osg::Matrix::rotate(_omega,0.0,1.0,0));
//	_root->preMult(osg::Matrix::rotate(_theta,0.0,0.0,1));
	_rootRot0->setMatrix(osg::Matrix::translate(_joueur->_rx,_joueur->_ry+3.4314,_joueur->_rz));
	_rootRot1->setMatrix(osg::Matrix::rotate(osg::inDegrees(-90.0f),1.0,0.0,0.0));
	_skyBox->update(_joueur->getAngleVue(),_phi);
}

void CMapNode::calculColision()
{
	osg::Vec2f posPlayer = _joueur->getVirtualPosition();

	osg::Vec3d position;
	position.x() = posPlayer.x();
	position.y() = posPlayer.y();
	position.z() = 0.0f;

	position = calculHeight(position);
	_joueur->setHeight(-position.z());
	_joueur->setVHeight(position.z());

	osg::Vec3d one;
	one.x() = position.x() + 0.001f;
	one.y() = position.y() - 0.001f;
	one.z() = position.z();

	osg::Vec3d two;
	two.x() = position.x() + 0.001f;
	two.y() = position.y() + 0.001f;
	two.z() = position.z();

	osg::Vec3d three;
	three.x() = position.x() - 0.001;
	three.y() = position.y() + 0.001f;
	three.z() = position.z();

	one = calculHeight(one);
	two = calculHeight(two);
	three = calculHeight(three);

	osg::Vec3d normal = calculNormal(one,two,three);

//	std::cout<<"normal x :"<< normal.x()<< " y :"<< normal.y()<<" z :"<<normal.z()<<std::endl;

	//calcul de phi
	osg::Vec3d ob;
	ob.x() = 0.0f;
	ob.y() = 0.0f;
	ob.z() = sqrt(normal.x()*normal.x() + normal.y()*normal.y() + normal.z()*normal.z());

	_phi = acos( (normal.x()*ob.x() + normal.y()*ob.y() + normal.z()*ob.z())/(normal.x()*normal.x() + normal.y()*normal.y() + normal.z()*normal.z()));
//	std::cout<<"phi = "<< _phi << std::endl;


	osg::Vec3d oc;
	oc.x() = 0.0f;
	oc.y() = 0.0f;
	oc.z() = sqrt(normal.x()*normal.x() + normal.y()*normal.y() + normal.z()*normal.z());

	_theta = acos( (normal.x()*oc.x() + normal.y()*oc.y() + normal.z()*oc.z())/(normal.x()*normal.x() + normal.y()*normal.y() + normal.z()*normal.z()));

	oc.x() = 0.0f;
	oc.y() = sqrt(normal.x()*normal.x() + normal.y()*normal.y() + normal.z()*normal.z());
	oc.z() = 0.0f;

	_omega = acos( (normal.x()*oc.x() + normal.y()*oc.y() + normal.z()*oc.z())/(normal.x()*normal.x() + normal.y()*normal.y() + normal.z()*normal.z()));
//	std::cout<<"theta = "<< _theta << std::endl;
	//_root->setMatrix(osg::Matrix::rotate(teta,0.0,0.0,0.0));
}

void CMapNode::addElementOnMap(float posx, float posy, int element)
{
	osg::ref_ptr<osg::MatrixTransform> matElement = new osg::MatrixTransform;
	osg::Vec3d position;
	position.x() = posx;
	position.y() = posy;
	position = calculHeight(position);
	position.z() -= 2.0f;


	switch (element)
	{
		case 0:
			matElement->setMatrix(osg::Matrix::translate(position));
			matElement->addChild(_treeElement->getNode());
			_elementOnmap->addChild(matElement.get());

		break;

		case 1:
			matElement->setMatrix(osg::Matrix::translate(position));
			matElement->addChild(_rockElement->getNode());
			_elementOnmap->addChild(matElement.get());
		break;
		case 2:
			position.z() += 2.7;
			matElement->setMatrix(osg::Matrix::translate(position));
			osg::ref_ptr<osg::MatrixTransform> rotElement = new osg::MatrixTransform;
			rotElement->addChild(_chaletElement->getNode());
			rotElement->setMatrix(osg::Matrix::rotate(osg::DegreesToRadians(90.0f),1.0f,0.0f,0.0f));
			rotElement->postMult(osg::Matrix::rotate(osg::DegreesToRadians(180.0f),0.0f,0.0f,1.0f));
			matElement->addChild(rotElement.get());
			_elementOnmap->addChild(matElement.get());
		break;
	}

}
